home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1999 / MacHack 1999.toast / The Hacks / ReaderMouse / LetterFind.c next >
Text File  |  1999-06-22  |  20KB  |  630 lines

  1. /* algorithm:
  2.     Find a letter in the immediate vicinity of the mouse.  
  3.         (the middle of the gworld data we are given. (what about edges?))
  4.         For each font and each point size
  5.             on a rectangle pointsize*2+1 tall and maxLetterWidth*2+1 wide
  6.             correlate all the letters,
  7.             store max correlation for each letter.  (Why not just max over font and point size?)
  8.             Store max correlation for font and point size allong with the letter with 
  9.                 max correlation and it's location.
  10.             (What about location?  Closeness to the pointer should also be important.)
  11.     Find the word that letter belongs to.
  12.         Assume that which ever font and point size has the highest correlation is the
  13.             font and point size for that word.
  14.         Using the location of the found letter perform a horizontal sweep of correlations 
  15.             to determine the rest of the letters in the word.
  16.         Speak the word.
  17.         Store the rectangle for the word and do no processing until the mouse leaves that
  18.             rectangle.  (Later.  Now we are processing movies.)
  19. */
  20. #include <math.h>
  21. #include <quickdraw.h>
  22.  
  23. #include "LetterFind.h"
  24. #include "WorkFunctions.h"
  25.  
  26. /* globals */
  27. short gPointSizes[NUM_POINT_SIZES] = {9, 10, 12, 14};
  28. short gFonts[NUM_FONTS] = {0,0,0};
  29. Str255 gFontNames[NUM_FONTS] = {"\pGeneva","\pMonaco","\pSystem"};//,"\pSystem"
  30. char gLetters[NUM_LETTERS+1] = 
  31.         " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";//',.""<>`1234567890[]~!@#$%^&*(){}/=?+-_\|;:";
  32. //       ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^     ^    ^    ^    ^    ^    ^    ^    ^    
  33. //       0    5    10   15   20   25   30   35   40   45   50   55    60   65   70   75   80   85   90   95
  34. FontData gFontDatas[NUM_FONTS * NUM_POINT_SIZES];
  35. short gBytesPerPixel = 1;
  36.  
  37. const short LETTER_PAD = LETTER_RIGHT_PAD + LETTER_LEFT_PAD;
  38. const short LETTER_KERNEL_DIFF = LETTER_PAD - LETTER_DIFF_MINUS;
  39.  
  40. const short gScreenHGrabSize = 120;
  41. const short gScreenVGrabSize = 50;
  42. GWorldPtr gScreenHDiff, gScreenVDiff;
  43.  
  44. void InitFontNumbers(void)
  45. {
  46.     short font;
  47.     
  48.     for (font=0; font<NUM_FONTS; font++)
  49.     {
  50.         GetFNum(gFontNames[font],&(gFonts[font]));
  51.     }
  52. }
  53.  
  54. /* initialize the fonts */
  55. OSErr InitializeFonts()
  56. {
  57.     FontData     *theFontData;
  58.     short         font, pointSize;
  59.     short         letter, kernelWidth, kernelOffset, baseLine;
  60.     int         gWorldWidth=0, pixelDepth;
  61.     GrafPtr        savePort;
  62.     GDHandle    saveGD;
  63.     Rect        boundsRect = {0,0,0,0};
  64.     OSErr        err;
  65.     FontInfo    theFontInfo;
  66.     // debugging info
  67.     Ptr    baseAddr;
  68.         
  69.     // Save the drawing environment.
  70.     GetPort(&savePort);
  71.     saveGD = GetGDevice();
  72.  
  73.     for (font=0; font < NUM_FONTS; font++)
  74.         for (pointSize=0; pointSize < NUM_POINT_SIZES; pointSize++)
  75.             gFontDatas[font * NUM_POINT_SIZES + pointSize].kernelBitmap = 0;
  76.  
  77.     InitFontNumbers();
  78.  
  79.     for(font=0; font < NUM_FONTS; font++)
  80.     {
  81.         for (pointSize = 0; pointSize < NUM_POINT_SIZES; pointSize++)
  82.         {
  83.             theFontData = &FONT_DATA(font,pointSize);
  84.  
  85.             // change font in original gworld, just so we keep the font in each gWorld
  86.             // the same as it's font data.  Not necessary, but it might help avoid accidental
  87.             // problems latter.
  88.             SetPort(savePort);
  89.  
  90.             // set font
  91.             TextFont(gFonts[font]);            
  92.             // set point size
  93.             TextSize(gPointSizes[pointSize]);
  94.             // set style to plain
  95.             TextFace(0);
  96.             // set mode to over write
  97.             TextMode(srcCopy);
  98.             
  99.             /* allocate gWorld */
  100.             gWorldWidth = 0;
  101.             for(letter=0;letter<NUM_LETTERS;letter++) gWorldWidth += CharWidth(gLetters[letter])+LETTER_PAD;
  102.             boundsRect.right = gWorldWidth;
  103.             GetFontInfo(&theFontInfo);
  104.             theFontData->maxHeight = theFontInfo.ascent+theFontInfo.descent;
  105.             // adding one space between letters. (+2) then each letter is one thiner since
  106.             // we're diffing, so edges are not meaningful. (-1) = (+1)
  107.             theFontData->maxWidth = theFontInfo.widMax + LETTER_PAD - LETTER_DIFF_MINUS; 
  108.             baseLine = theFontInfo.ascent;
  109.             boundsRect.bottom = theFontData->maxHeight;
  110.  
  111.             //pixelDepth = ((CGrafPort*)savePort)->portPixMap[0]->pixelSize;
  112.             pixelDepth = gBytesPerPixel * 8;
  113.             err = NewGWorld(&(theFontData->kernelBitmap), pixelDepth, &boundsRect, nil, nil, 0);    
  114.             if (err != noErr || theFontData->kernelBitmap == nil) goto initializeFontsDeath;
  115.             SetPort((GrafPort*)(theFontData->kernelBitmap));
  116.             
  117.             baseAddr = GetPixBaseAddr(GetGWorldPixMap(theFontData->kernelBitmap));
  118.             // White out gWorld so we can add spaces between the letters if we want.
  119.             //PenMode(patCopy);
  120.             //ForeColor(whiteColor);
  121.             EraseRect(&boundsRect);
  122.             //ForeColor(blackColor);
  123.             baseAddr = GetPixBaseAddr(GetGWorldPixMap(theFontData->kernelBitmap));
  124.             
  125.             //set font
  126.             TextFont(gFonts[font]);            
  127.             TextSize(gPointSizes[pointSize]);
  128.             TextFace(0);
  129.             TextMode(srcCopy);
  130.  
  131.             theFontData->kernelOffset[0] = 0;
  132.             kernelOffset = 0;
  133.             for(letter = 0; letter < NUM_LETTERS; letter++)
  134.             {
  135.                 // draw letter over one pixel to put a space on each side.
  136.                 MoveTo(kernelOffset+LETTER_LEFT_PAD, baseLine); 
  137.                 DrawChar(gLetters[letter]);
  138.  
  139.                 kernelWidth = CharWidth(gLetters[letter]); 
  140.                 kernelWidth+=LETTER_PAD; // add extra space on each side of letters.
  141.                 kernelWidth-=LETTER_DIFF_MINUS; // remove boundary since we're doing diffing.
  142.                 theFontData->kernelWidth[letter] = kernelWidth;
  143.                 kernelOffset += kernelWidth + LETTER_DIFF_MINUS;
  144.                 
  145.                 if(letter!=(NUM_LETTERS-1)) 
  146.                     theFontData->kernelOffset[letter+1] = kernelOffset;
  147.                 
  148.             }    
  149.             ConvertToHDiffKernel(theFontData->kernelBitmap);        
  150.         }        
  151.     }
  152.     
  153.     // Initialize Difference GWorlds.
  154.     err = InitializeDifferenceGWorlds();
  155.     if (err != noErr) goto initializeFontsDeath;
  156.     
  157.     // Restore the drawing environment.
  158.     SetPort(savePort);
  159.     SetGDevice(saveGD);
  160.     
  161.     return 0;
  162.     
  163. initializeFontsDeath:
  164.     SetPort(savePort);
  165.     SetGDevice(saveGD);
  166.     for (font=0; font < NUM_FONTS; font++)
  167.         for (pointSize=0; pointSize < NUM_POINT_SIZES; pointSize++)
  168.             if(FONT_DATA(font,pointSize).kernelBitmap != nil)
  169.                 DisposeGWorld(FONT_DATA(font,pointSize).kernelBitmap);
  170.  
  171.     return -1;
  172. }
  173.  
  174. OSErr InitializeDifferenceGWorlds(void)
  175. {
  176.     OSErr    err;
  177.     Rect    boundsRect = {0,0,gScreenVGrabSize,gScreenHGrabSize};
  178.     short    pixelDepth = 8;
  179.     
  180.     err = NewGWorld(&gScreenVDiff, pixelDepth, &boundsRect, nil, nil, 0);    
  181.     if (err != noErr || gScreenVDiff == nil) goto initializeDifferenceGWorldsDeath;
  182.     err = NewGWorld(&gScreenHDiff, pixelDepth, &boundsRect, nil, nil, 0);    
  183.     if (err != noErr || gScreenHDiff == nil) goto initializeDifferenceGWorldsDeath;
  184.     
  185.     return noErr;
  186.     
  187. initializeDifferenceGWorldsDeath:
  188.     if (gScreenHDiff != nil) DisposeGWorld(gScreenHDiff);
  189.     if (gScreenVDiff != nil) DisposeGWorld(gScreenVDiff);
  190.     
  191.     return -1;
  192. }
  193.  
  194.  
  195.  
  196. /* currently only handles 8 and 16 bits. */
  197. void ConvertToHDiffKernel(GWorldPtr inGWorld)
  198. {
  199.     PixMapHandle pixMap;
  200.     short    rowBytes;
  201.     Ptr        baseAddr;
  202.     short    x,y;
  203.     short    bitsPerPixel;
  204.     short    height;
  205.     
  206.     pixMap = GetGWorldPixMap(inGWorld);
  207.     LockPixels(pixMap);
  208.     rowBytes = ROWBYTES(pixMap[0]->rowBytes);
  209.     height = pixMap[0]->bounds.bottom - pixMap[0]->bounds.top;
  210.     baseAddr = pixMap[0]->baseAddr;
  211.     bitsPerPixel = pixMap[0]->pixelSize;
  212.     switch(bitsPerPixel)
  213.     {
  214.         case 8:
  215.             for (y=0; y <height; y++)
  216.                 for (x=0; x<rowBytes-1; x++)
  217.                     if (*(baseAddr + y*rowBytes + x) != *(baseAddr + y*rowBytes + x +1))
  218.                         *(baseAddr + y*rowBytes + x) = 1;
  219.                     else
  220.                         *(baseAddr + y*rowBytes + x) = -1;
  221.             break;
  222.                     
  223.         case 16:
  224.             for (y=0; y <height; y++)
  225.                 for (x=0; x<rowBytes-2; x+=2)
  226.                     if ((*(baseAddr + y*rowBytes + x) & MASK_16) != (*(baseAddr + y*rowBytes + x+2) & MASK_16))
  227.                         *(short*)(baseAddr + y*rowBytes + x) = 1;
  228.                     else
  229.                         *(short*)(baseAddr + y*rowBytes + x) = -1;
  230.             break;
  231.     }
  232.     
  233.     UnlockPixels(pixMap);
  234.                     
  235. }
  236.  
  237.  
  238. /* currently only handles 16 bit color. */
  239. void HorizontalDifference(GWorldPtr screenGWorld)
  240. {
  241.     PixMapHandle    screenPixMapH, diffPixMapH;
  242.     short            screenRowBytes, diffRowBytes, bitDepth;
  243.     short            x,y,diffY;
  244.     Ptr                diffBits;
  245.     short            *screenBits; // 16 bit value
  246.     Rect    screenBounds, diffBounds;
  247.     long            diffSum;
  248.     char            diffMax=0, diffMin=255;
  249.  
  250.     
  251.     screenPixMapH = GetGWorldPixMap(screenGWorld);
  252.     LockPixels(screenPixMapH);    
  253.     bitDepth = screenPixMapH[0]->pixelSize;
  254.     screenRowBytes = ROWBYTES(screenPixMapH[0]->rowBytes);
  255.     
  256.     diffPixMapH = GetGWorldPixMap(gScreenHDiff);
  257.     LockPixels(diffPixMapH);
  258.     diffRowBytes = ROWBYTES(diffPixMapH[0]->rowBytes);
  259.     
  260.     RECT_EQUAL(screenBounds, screenPixMapH[0]->bounds);
  261.     RECT_EQUAL(diffBounds, gScreenHDiff->portRect)
  262.     
  263.     CommonRect(&diffBounds, &screenBounds);
  264.     
  265.     for(y = screenBounds.top,diffY = diffBounds.top; y < screenBounds.bottom; y++,diffY++)
  266.     {
  267.         screenBits = (short*)(screenPixMapH[0]->baseAddr + y*screenRowBytes) + screenBounds.left;
  268.         diffBits = diffPixMapH[0]->baseAddr + diffY*diffRowBytes + diffBounds.left;
  269.         for(x=screenBounds.left; x < screenBounds.right-1; x++)
  270.         {
  271.             *diffBits =   ABS(RPART16(*screenBits)-RPART16(*(screenBits+1)))
  272.                         + ABS(GPART16(*screenBits)-GPART16(*(screenBits+1)))
  273.                         + ABS(BPART16(*screenBits)-BPART16(*(screenBits+1)));
  274.             
  275.             diffSum += *diffBits;
  276.             
  277.             if (*diffBits > diffMax) 
  278.                 diffMax = *diffBits;
  279.             else if (*diffBits < diffMin)
  280.                 diffMin = *diffBits;
  281.                 
  282.             screenBits++;
  283.             diffBits++;
  284.         }
  285.     }
  286.     
  287.     /* I want the maxDiff and the minDiff to be the highest and lowest values respectively.  
  288.         I was just subtracting the mid possible
  289.         difference value in the loop above, but in order for this to work well on light
  290.         text on a light background or dark text on a dark background I've decided to be
  291.         a little more picky with the difference averaging. 
  292.         This won't help us with light text near dark text, but it won't hurt the high 
  293.         contrast text either.
  294.         Or maybe I just want to threshold on the average contrast.  That won't hurt
  295.         black on white either, since they will be max or zero contrast...
  296.         Or maybe I should threshold everything greater than zero to max.
  297.         */
  298.         
  299.     //diffMid = diffSum/((screenBounds.bottom-screenBounds.top)*(screenBounds.right-screenBounds.left));
  300.     
  301.     for(y = screenBounds.top,diffY = diffBounds.top; y < screenBounds.bottom; y++,diffY++)
  302.     {
  303.         screenBits = (short*)(screenPixMapH[0]->baseAddr + y*screenRowBytes) + screenBounds.left;
  304.         diffBits = diffPixMapH[0]->baseAddr + diffY*diffRowBytes + diffBounds.left;
  305.         for(x=screenBounds.left; x < screenBounds.right-1; x++)
  306.         {
  307.  
  308.             // a: threshold everything over a small min value to -max over to max
  309.             if (*diffBits >= 5) 
  310.                 *diffBits = MID_DIFF_VALUE_16;
  311.             else
  312.                 *diffBits = -1*MID_DIFF_VALUE_16;
  313.             
  314. //            if(*diffBits >= 0) (*diffBits)++;
  315.  
  316.             screenBits++;
  317.             diffBits++;
  318.         }
  319.     }
  320.     
  321.     UnlockPixels(screenPixMapH);
  322.     UnlockPixels(diffPixMapH);
  323.     
  324. }
  325.  
  326.  
  327. /* Pick the most likely font and point size. 
  328.     Find a letter in the immediate vicinity of the mouse.  
  329.         (the middle of the gworld data we are given. (what about edges?))
  330.         For each font and each point size
  331.             on a rectangle pointsize*2+1 tall and maxLetterWidth*2+1 wide
  332.             correlate all the letters,
  333.             store max correlation for each letter.  (Why not just max over font and point size?)
  334.             Store max correlation for font and point size allong with the letter with 
  335.                 max correlation and it's location.
  336.             (What about location?  Closeness to the pointer should also be important.)
  337. */
  338.  
  339. void PickFontNPointDiff16(WordData *bestWord)
  340. {
  341.     short    midX,midY,kernelWidth,kernelHeight;
  342.     Rect    searchRect,portRect;
  343.     short    font,pointSize,letter;
  344.     FontData    *theFontData;
  345.     GWorldPtr    fontGWorld;
  346.     PixMapHandle    fontPixMap,screenPixMap;
  347.     short        screenRowBytes,fontRowBytes;
  348.     Ptr        screenPixData, fontPixData, kernelPixData;
  349.     float    fontMaxLetterScore;
  350.     short    fontMaxLetter, fontMaxLetterX, fontMaxLetterY;
  351.     LetterData theLetter;
  352.     
  353.     screenPixMap = GetGWorldPixMap(gScreenHDiff);
  354.     LockPixels(screenPixMap);
  355.     screenRowBytes = ROWBYTES(screenPixMap[0]->rowBytes);
  356.     screenPixData = screenPixMap[0]->baseAddr;
  357.     
  358.     RECT_EQUAL(portRect,gScreenHDiff->portRect);
  359.     midY = (portRect.bottom - portRect.top)/2;
  360.     midX = (portRect.right - portRect.left)/2;
  361.  
  362.     for (font=0; font<NUM_FONTS; font++)
  363.     {
  364.         for (pointSize=0; pointSize<NUM_POINT_SIZES; pointSize++)
  365.         {
  366.             theFontData = &FONT_DATA(font,pointSize);
  367.             
  368.             fontGWorld = theFontData->kernelBitmap;
  369.             fontPixMap = GetGWorldPixMap(fontGWorld);
  370.             LockPixels(fontPixMap);
  371.             fontRowBytes = ROWBYTES(fontPixMap[0]->rowBytes);
  372.             fontPixData = fontPixMap[0]->baseAddr;
  373.             
  374.             // Find search rect.
  375.             kernelHeight = theFontData->maxHeight;
  376.             searchRect.top = MAX(portRect.top, midY-kernelHeight);
  377.             searchRect.bottom = MIN(portRect.bottom-kernelHeight, midY+kernelHeight - kernelHeight);
  378.             
  379.             fontMaxLetter = 0;
  380.             fontMaxLetterScore = 0;
  381.             
  382.             for (letter=1; letter<NUM_LETTERS; letter++)
  383.             {
  384.                 kernelWidth = theFontData->kernelWidth[letter];
  385.                 searchRect.left = MAX(portRect.left, midX-kernelWidth);
  386.                 searchRect.right = MIN(portRect.right-kernelWidth, midX+kernelWidth-kernelWidth);
  387.                 kernelPixData = fontPixData + theFontData->kernelOffset[letter];
  388.                 
  389.                 Correlate(&theLetter,&searchRect, kernelHeight, kernelWidth,
  390.                             kernelPixData, screenPixData, 
  391.                             fontRowBytes, screenRowBytes);
  392.  
  393.                 if (theLetter.letterScore > fontMaxLetterScore)
  394.                 {
  395.                     fontMaxLetterScore = theLetter.letterScore;
  396.                     fontMaxLetter = letter;
  397.                     fontMaxLetterX = theLetter.x;
  398.                     fontMaxLetterY = theLetter.y;
  399.                 }
  400.             }
  401.             
  402.             if (fontMaxLetterScore > bestWord->aveLetterScore)
  403.             {
  404.                 bestWord->aveLetterScore = fontMaxLetterScore;
  405.                 bestWord->letters[MID_WORD_LETTER] = fontMaxLetter;
  406.                 bestWord->font = font;
  407.                 bestWord->pointSize = pointSize;
  408.                 bestWord->wordBounds.left = fontMaxLetterX + LETTER_LEFT_PAD;
  409.                 bestWord->wordBounds.top = fontMaxLetterY;
  410.                 bestWord->wordBounds.right = NEXT_LETTER_OFFSET(fontMaxLetterX, theFontData->kernelWidth[fontMaxLetter]);
  411.                 bestWord->wordBounds.bottom = fontMaxLetterY + theFontData->maxHeight;
  412.             }
  413.             
  414.             UnlockPixels(fontPixMap);
  415.         }
  416.     }
  417.     
  418.     
  419.     UnlockPixels(screenPixMap);
  420. }
  421.  
  422. void Correlate(LetterData *bestLetter,Rect *searchRect, 
  423.                 short kernelHeight, short kernelWidth,
  424.                 Ptr kernelPixData, Ptr screenPixData, 
  425.                 short fontRowBytes, short screenRowBytes)
  426. {
  427.     short x,y,i,j;
  428.     long    letterScore,maxLetterScore; // keep from casting short product to float!
  429.     Ptr kernelPixel, screenPixel, screenPixelLetterStart;
  430.     short screenRowIncrement,kernelRowIncrement;
  431.     
  432.     screenRowIncrement = screenRowBytes - kernelWidth;
  433.     kernelRowIncrement = fontRowBytes - kernelWidth;
  434.     
  435.     maxLetterScore = 0;
  436.         
  437.     for (y=searchRect->top; y<searchRect->bottom + 1; y++)
  438.     {
  439.         screenPixelLetterStart = screenPixData + y*screenRowBytes;
  440.         for (x=searchRect->left; x<searchRect->right + 1; x++)
  441.         {
  442.             kernelPixel = kernelPixData;
  443.             screenPixel = screenPixelLetterStart + x;
  444.             letterScore = 0;
  445.             for (j=0; j<kernelHeight; j++)
  446.             {
  447.                 for (i=0; i<kernelWidth; i++)
  448.                 {
  449.                     letterScore += *screenPixel * *kernelPixel;
  450.                     
  451.                     screenPixel++;
  452.                     kernelPixel++;
  453.                 }
  454.                 kernelPixel += kernelRowIncrement;
  455.                 screenPixel += screenRowIncrement;
  456.             }
  457.             
  458.             if (letterScore > maxLetterScore)
  459.             {
  460.                 maxLetterScore = letterScore;
  461.                 bestLetter->x = x;
  462.                 bestLetter->y = y;
  463.             }
  464.         }
  465.     }
  466.     
  467.     bestLetter->letterScore = (float)maxLetterScore/(float)(kernelHeight * kernelWidth);
  468. }
  469.  
  470. /* pointer letter is the letter that the pointer is most nearly over. */
  471. void GrowWord(WordData *bestWord)
  472. {
  473.     LetterData nextLetter,lastLetter;
  474.     PixMapHandle    screenPixMap, fontPixMap;
  475.     Ptr                screenPixData, fontPixData;
  476.     short    screenRowBytes, fontRowBytes;
  477.     Rect    *screenRect;
  478.     FontData    *theFontData;
  479.     short    font, pointSize;
  480.     short    done,wordIndex=0;
  481.     short    searchLeft;
  482.     
  483.     font = bestWord->font;
  484.     pointSize = bestWord->pointSize;
  485.     theFontData = &FONT_DATA(font,pointSize);
  486.     
  487.     /* lock relevant gWorlds */
  488.     
  489.     /* set up screen stuff*/
  490.     screenPixMap = GetGWorldPixMap(gScreenHDiff);
  491.     LockPixels(screenPixMap);
  492.     screenRowBytes = ROWBYTES(screenPixMap[0]->rowBytes);
  493.     screenPixData = screenPixMap[0]->baseAddr;
  494.     screenRect = &(gScreenHDiff->portRect);
  495.  
  496.     /* set up font stuff */
  497.     fontPixMap = GetGWorldPixMap(theFontData->kernelBitmap);
  498.     LockPixels(fontPixMap);
  499.     fontRowBytes = ROWBYTES(fontPixMap[0]->rowBytes);
  500.     fontPixData = fontPixMap[0]->baseAddr;
  501.  
  502.     /* first extend word to the right, then the left
  503.         If we extend tothe left first then the bounding box of the word will change
  504.         so when we set last Letter to it to start the right half, that's fucked up. */
  505.     for (searchLeft = 0; searchLeft <2; searchLeft++)
  506.     {
  507.         LETTER_EQUAL_WORD(lastLetter, *bestWord);
  508.         LETTER_EQUAL(nextLetter,lastLetter);
  509.         done = 0;
  510.         do
  511.         {
  512.             FindNextLetter(&nextLetter, searchLeft, screenPixData, screenRowBytes, screenRect, fontPixData, fontRowBytes);
  513.             if (nextLetter.letterScore < CONFIDANT_SCORE) nextLetter.letter = 0;
  514.             if ((nextLetter.x != lastLetter.x)
  515.                 && (nextLetter.letter > 0) && (nextLetter.letter <= MAX_ALPHA_LETTER))
  516.             {
  517.                 if (searchLeft)
  518.                 {
  519.                     (bestWord->leftLetters)++;
  520.                     bestWord->letters[MID_WORD_LETTER- (bestWord->leftLetters)] = nextLetter.letter;
  521.                     bestWord->wordBounds.left = nextLetter.x;
  522.                 }
  523.                 else
  524.                 {
  525.                     (bestWord->rightLetters)++;
  526.                     bestWord->letters[MID_WORD_LETTER+ (bestWord->rightLetters)] = nextLetter.letter;
  527.                     bestWord->wordBounds.right = NEXT_LETTER_OFFSET(nextLetter.x,theFontData->kernelWidth[nextLetter.letter]);
  528.                 }
  529.                 UPDATE_SCORE(*bestWord,nextLetter.letterScore);
  530.             }
  531.             else
  532.                 done = 1;
  533.             LETTER_EQUAL(lastLetter,nextLetter);
  534.         }while(!done);
  535.     }
  536.     
  537.     UnlockPixels(fontPixMap);
  538.     UnlockPixels(screenPixMap);
  539. }
  540.  
  541. /* assumes the relevant gWorlds are locked, 
  542.     (screen horizontal difference & selected font-pointsize) */
  543. void FindNextLetter(LetterData *refLetter, short searchLeft,
  544.                             Ptr screenPixData, short screenRowBytes, Rect *screenRect,
  545.                             Ptr fontPixData, short fontRowBytes)
  546. {
  547.     Rect    searchRect; // top left coords to search over.
  548.     FontData *theFont;
  549.     short    letter, kernelHeight, kernelWidth, refKernelWidth;
  550.     LetterData    theLetter;
  551.     Ptr        kernelPixData;
  552.     short    left,right;
  553.     
  554.     theFont = &FONT_DATA(refLetter->font, refLetter->pointSize);
  555.     kernelHeight = theFont->maxHeight;
  556.     refKernelWidth = theFont->kernelWidth[refLetter->letter];
  557.     
  558.     /* set up horizontal search, allow jitter of 20% of the pointSize? min of +-2? */
  559.     left = refLetter->x - kernelHeight/JITTER_DIVIDER;
  560.     right = refLetter->x + kernelHeight/JITTER_DIVIDER;
  561.     /* searchRect.right is right - letterWidth, so searchRect.right + letterWidth is
  562.         > screenRect.left since refX + plusMinus is. */
  563.     
  564.     /* currently assume zero vertical jitter. */
  565.     searchRect.top = refLetter->y;
  566.     searchRect.bottom = refLetter->y;
  567.     
  568.     
  569.     refLetter->letterScore = 0;
  570.     /* correlate each letter over that interval. */
  571.     for (letter = 0; letter < NUM_LETTERS; letter++)
  572.     {
  573.         kernelWidth = theFont->kernelWidth[letter];
  574.         
  575.         if(searchLeft) // searching to the left
  576.         {
  577.             searchRect.right = LAST_LETTER_OFFSET(right, kernelWidth);
  578.             searchRect.left = LAST_LETTER_OFFSET(left, kernelWidth);
  579.             if (searchRect.left < screenRect->left)
  580.                 searchRect.left = screenRect->left;
  581.         }
  582.         else // searching to the right
  583.         { /* when optimizing this should be taken out of the loop.  I want to keep it
  584.             symetric for debugging. */
  585.             searchRect.right = NEXT_LETTER_OFFSET(right, refKernelWidth);
  586.             searchRect.left = NEXT_LETTER_OFFSET(left, refKernelWidth);
  587.             if (searchRect.right > screenRect->right)
  588.                 searchRect.right = screenRect->right;
  589.         }
  590.         
  591.         kernelPixData = fontPixData + theFont->kernelOffset[letter];
  592.         
  593.         Correlate(&theLetter,&searchRect, 
  594.             kernelHeight, kernelWidth,
  595.             kernelPixData, screenPixData, 
  596.             fontRowBytes, screenRowBytes);
  597.  
  598.         if (theLetter.letterScore > refLetter->letterScore)
  599.         {
  600.             refLetter->letterScore = theLetter.letterScore;
  601.             refLetter->letter = letter;
  602.             refLetter->x = theLetter.x;
  603.             refLetter->y = theLetter.y;
  604.         }
  605.     }
  606. }
  607.  
  608. void MyFindWord (WordData *bestWord, GWorldPtr inGWorld)
  609. {
  610.     HorizontalDifference(inGWorld);
  611.         
  612.     ZERO_WORD(*bestWord);
  613.  
  614.     PickFontNPointDiff16(bestWord);
  615.     
  616.     if (bestWord->aveLetterScore < CONFIDANT_SCORE) return;
  617.     
  618.     GrowWord(bestWord);
  619.  
  620. void CopyWordToPascalString(Str255 inString, WordData *inWord)
  621. {
  622.     short stringIndex = 0,i;
  623.     
  624.     inString[stringIndex++] = WORD_LENGTH(*inWord);
  625.     for (i = WORD_LEFT_INDEX(*inWord); i <= WORD_RIGHT_INDEX(*inWord); i++)
  626.         inString[stringIndex++] = gLetters[inWord->letters[i]];
  627. }
  628.  
  629.